package com.nancy.springioc.context.support;

import com.nancy.springioc.bean.factory.BeanDefinition;
import com.nancy.springioc.bean.factory.MutablePropertyValue;
import com.nancy.springioc.bean.factory.PropertyValue;
import com.nancy.springioc.bean.factory.support.BeanDefinitionRegistry;
import com.nancy.springioc.bean.factory.support.XmlBeanDefinitionReader;
import com.nancy.springioc.util.BeanUtil;

import java.lang.reflect.Method;

/**
 * IOC容器具体的实现子类
 * 这里实现的xml方式
 * properties配置文件略过
 */
public class ClassPathXmlApplicationContext extends AbstractApplicationContext {

    public ClassPathXmlApplicationContext(String configLocation) throws Exception {
        this.configLocation = configLocation;
        beanDefinitionReader = new XmlBeanDefinitionReader();
        refresh();
    }

    @Override
    public Object getBean(String beanName) throws Exception {
        //从容器中获取bean,如果不存在，则创建
        Object bean = singletonBeanMap.get(beanName);
        if (bean != null) {
            return bean;
        }
        //创建bean
        //1.获取注册器
        BeanDefinitionRegistry registry = beanDefinitionReader.getRegistry();
        //2.从注册器中获取beanDefinition
        BeanDefinition beanDefinition = registry.getBeanDefinition(beanName);
        //3.利用反射机制创建bean实例
        Class<?> clazz = Class.forName(beanDefinition.getClassName());
        Object beanInstance = clazz.newInstance();
        if (beanInstance != null) {
            //4.利用beanDefinition中的属性，给bean设置属性,即依赖注入
            MutablePropertyValue propertyValue = beanDefinition.getPropertyValue();
            if (propertyValue != null) {
                for (PropertyValue pv : propertyValue) {
                    String pvName = pv.getName();
                    String pvValue = pv.getValue();
                    String pvRef = pv.getRef();

                    //a.如果pvRef不为空，则获取beanRef
                    if (pvRef != null && !"".equals(pvRef)) {
                        Object beanRef = getBean(pvRef);
                        //获取set方法名称
                        String setMethodName = BeanUtil.getSetBean(pvName);
                        //通过反射机制,获取所有方法
                        Method[] methods = clazz.getMethods();
                        for (Method m : methods) {
                            //判断方法名称是否与set方法名称一致，为什么要判断?因为只拿到了set方法名称，
                            // 没有拿到set方法，具体的调用的是set方法体，所以需要通过反射机制获取到set方法体，进而执行set方法体
                            if (m.getName().equals(setMethodName)) {
                                //调用set方法
                                m.invoke(beanInstance, beanRef);
                            }
                        }

                    }

                    //b.如果pvValue不为空，则给bean设置属性
                    if (pvValue != null && !"".equals(pvValue)) {
                        String setMethodName = BeanUtil.getSetBean(pvName);
                        Method method = clazz.getMethod(setMethodName, String.class);
                        //执行set方法，进行属性注入，赋值
                        method.invoke(beanInstance, pvValue);
                    }

                }
            }
            //5.将bean放入容器中
            singletonBeanMap.put(beanName, beanInstance);
            return beanInstance;
        }
        return null;
    }

    @Override
    public <T> T getBean(String beanName, Class<? extends T> clazz) throws Exception {
        Object bean = getBean(beanName);
        if (bean == null) {
            return null;
        }
        return clazz.cast(bean);
    }
}
